Spring @Aspect、@Before、@After 注解实现 AOP 切面功能

您所在的位置:网站首页 aspect JAVApom文件中会报错 Spring @Aspect、@Before、@After 注解实现 AOP 切面功能

Spring @Aspect、@Before、@After 注解实现 AOP 切面功能

2024-07-13 20:23:00| 来源: 网络整理| 查看: 265

目录

Spring AOP 注解概述

@Aspect 快速入门

execution 切点表达式 拦截指定类的方法

@Pointcut("@annotation(xx)")  拦截拥有指定注解的方法

环绕通知 实现开关目标方法

Spring AOP 注解概述

1、Spring 的 AOP 功能除了在配置文件中配置一大堆的配置,比如切入点、表达式、通知等等以外,使用注解的方式更为方便快捷,特别是 Spring boot 出现以后,基本不再使用原先的 beans.xml 等配置文件了,而都推荐注解编程。

@Aspect切面声明,标注在类、接口(包括注解类型)或枚举上。@Pointcut

切入点声明,即切入到哪些目标类的目标方法。既可以用 execution 切点表达式, 也可以是 annotation 指定拦截拥有指定注解的方法.

value 属性指定切入点表达式,默认为 "",用于被通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式

@Before

前置通知, 在目标方法(切入点)执行之前执行。

value 属性绑定通知的切入点表达式,可以关联切入点声明,也可以直接设置切入点表达式

注意:如果在此回调方法中抛出异常,则目标方法不会再执行,会继续执行后置通知 -> 异常通知。

@After后置通知, 在目标方法(切入点)执行之后执行@AfterReturning

返回通知, 在目标方法(切入点)返回结果之后执行.

pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 ""

@AfterThrowing

异常通知, 在方法抛出异常之后执行, 意味着跳过返回通知

pointcut 属性绑定通知的切入点表达式,优先级高于 value,默认为 ""

注意:如果目标方法自己 try-catch 了异常,而没有继续往外抛,则不会进入此回调函数

@Around

环绕通知:目标方法执行前后分别执行一些代码,类似拦截器,可以控制目标方法是否继续执行。

通常用于统计方法耗时,参数校验等等操作。

正常流程:【环绕通知-前】-> 【前置通知】-> 【返回通知】-> 【后置通知】->【环绕通知-后】。 

2、上面这些 AOP 注解都是位于如下所示的 aspectjweaver 依赖中:

 3、对于习惯了 Spring 全家桶编程的人来说,并不是需要直接引入 aspectjweaver 依赖,因为 spring-boot-starter-aop 组件默认已经引用了 aspectjweaver 来实现  AOP 功能。换句话说 Spring 的 AOP 功能就是依赖的 aspectjweaver !

org.springframework.boot spring-boot-starter-aop 2.1.4.RELEASE

4、AOP 底层是通过 Spring 提供的的动态代理技术实现的,在运行期间动态生成代理对象,代理对象方法执行时进行增强功能的介入,再去调用目标对象的方法,从而完成功能的增强。主要使用 JDK 动态代理与 Cglib 动态代理。

5、所以如果目标类不是 Spring 组件,则无法拦截,如果是 类名.方法名 方式调用,也无法拦截。

@Aspect 快速入门

1、@Aspect 常见用于记录日志、异常集中处理、权限验证、Web 参数校验、事务处理等等

2、要想把一个类变成切面类,只需3步:

1)在类上使用 @Aspect 注解使之成为切面类

2)切面类需要交由 Sprign 容器管理,所以类上还需要有 @Service、@Repository、@Controller、@Component  等注解 2)在切面类中自定义方法接收通知

3、AOP 的含义就不再累述了,下面直接上示例:

/** * 切面注解 Aspect 使用入门 * 1、@Aspect:声明本类为切面类 * 2、@Component:将本类交由 Spring 容器管理 * 3、@Order:指定切入执行顺序,数值越小,切面执行顺序越靠前,默认为 Integer.MAX_VALUE * * @author wangMaoXiong * @version 1.0 * @date 2020/8/20 19:22 */ @Aspect @Order(value = 999) @Component public class AspectHelloWorld { private static final Logger LOG = LoggerFactory.getLogger(AspectHelloWorld.class); /** * @Pointcut :切入点声明,即切入到哪些目标方法。value 属性指定切入点表达式,默认为 ""。 * 用于被下面的通知注解引用,这样通知注解只需要关联此切入点声明即可,无需再重复写切入点表达式 *

* 切入点表达式常用格式举例如下: * - * com.wmx.aspect.EmpService.*(..)):表示 com.wmx.aspect.EmpService 类中的任意方法 * - * com.wmx.aspect.*.*(..)):表示 com.wmx.aspect 包(不含子包)下任意类中的任意方法 * - * com.wmx.aspect..*.*(..)):表示 com.wmx.aspect 包及其子包下任意类中的任意方法 *

* value 的 execution 可以有多个,使用 || 隔开. */ @Pointcut(value = "execution(* com.wmx.hb.controller.DeptController.*(..)) " + "|| execution(* com.wmx.hb.controller.EmpController.*(..))") private void aspectPointcut() { } /** * 前置通知:目标方法执行之前执行以下方法体的内容。 * value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式 * * * @param joinPoint:提供对连接点处可用状态和有关它的静态信息的反射访问

* * * Object[] getArgs():返回此连接点处(目标方法)的参数,目标方法无参数时,返回空数组 * * * Signature getSignature():返回连接点处的签名。 * * * Object getTarget():返回目标对象 * * * Object getThis():返回当前正在执行的对象 * * * StaticPart getStaticPart():返回一个封装此连接点的静态部分的对象。 * * * SourceLocation getSourceLocation():返回与连接点对应的源位置 * * * String toLongString():返回连接点的扩展字符串表示形式。 * * * String toShortString():返回连接点的缩写字符串表示形式。 * * * String getKind():返回表示连接点类型的字符串 * * *

*/ @Before(value = "aspectPointcut()") public void aspectBefore(JoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); Object target = joinPoint.getTarget(); Object aThis = joinPoint.getThis(); JoinPoint.StaticPart staticPart = joinPoint.getStaticPart(); SourceLocation sourceLocation = joinPoint.getSourceLocation(); String longString = joinPoint.toLongString(); String shortString = joinPoint.toShortString(); LOG.debug("【前置通知】" + "args={},signature={},target={},aThis={},staticPart={}," + "sourceLocation={},longString={},shortString={}" , Arrays.asList(args), signature, target, aThis, staticPart, sourceLocation, longString, shortString); } /** * 后置通知:目标方法执行之后执行以下方法体的内容,不管目标方法是否发生异常。 * value:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式 */ @After(value = "aspectPointcut()") public void aspectAfter(JoinPoint joinPoint) { LOG.debug("【后置通知】kind={}", joinPoint.getKind()); } /** * 返回通知:目标方法返回后执行以下代码 * value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式 * pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 "" * returning 属性:通知签名中要将返回值绑定到的参数的名称,默认为 "" * * @param joinPoint :提供对连接点处可用状态和有关它的静态信息的反射访问 * @param result :目标方法返回的值,参数名称与 returning 属性值一致。无返回值时,这里 result 会为 null. */ @AfterReturning(pointcut = "aspectPointcut()", returning = "result") public void aspectAfterReturning(JoinPoint joinPoint, Object result) { LOG.debug("【返回通知】,shortString={},result=", joinPoint.toShortString(), result); } /** * 异常通知:目标方法发生异常的时候执行以下代码,此时返回通知不会再触发 * value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式 * pointcut 属性:绑定通知的切入点表达式,优先级高于 value,默认为 "" * throwing 属性:与方法中的异常参数名称一致, * * @param ex:捕获的异常对象,名称与 throwing 属性值一致 */ @AfterThrowing(pointcut = "aspectPointcut()", throwing = "ex") public void aspectAfterThrowing(JoinPoint jp, Exception ex) { String methodName = jp.getSignature().getName(); if (ex instanceof ArithmeticException) { LOG.error("【异常通知】" + methodName + "方法算术异常(ArithmeticException):" + ex.getMessage()); } else { LOG.error("【异常通知】" + methodName + "方法异常:" + ex.getMessage()); } } /** * 环绕通知 * 1、@Around 的 value 属性:绑定通知的切入点表达式。可以关联切入点声明,也可以直接设置切入点表达式 * 2、Object ProceedingJoinPoint.proceed(Object[] args) 方法:继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常. * 3、假如目标方法是控制层接口,则本方法的异常捕获与否都不会影响目标方法的事务回滚 * 4、假如目标方法是控制层接口,本方法 try-catch 了异常后没有继续往外抛,则全局异常处理 @RestControllerAdvice 中不会再触发 * * @param joinPoint * @return * @throws Throwable */ @Around(value = "aspectPointcut()") public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable { this.checkRequestParam(joinPoint); StopWatch stopWatch = StopWatch.createStarted(); LOG.debug("【环绕通知】执行接口开始,方法={},参数={} ", joinPoint.getSignature(), Arrays.asList(joinPoint.getArgs()).toString()); //继续下一个通知或目标方法调用,返回处理结果,如果目标方法发生异常,则 proceed 会抛异常. //如果在调用目标方法或者下一个切面通知前抛出异常,则不会再继续往后走. Object proceed = joinPoint.proceed(joinPoint.getArgs()); stopWatch.stop(); long watchTime = stopWatch.getTime(); LOG.debug("【环绕通知】执行接口结束,方法={}, 返回值={},耗时={} (毫秒)", joinPoint.getSignature(), proceed, watchTime); return proceed; } /** * 参数校验,防止 SQL 注入 * * @param joinPoint */ private void checkRequestParam(ProceedingJoinPoint joinPoint) { Object[] args = joinPoint.getArgs(); if (args == null || args.length


【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭